/*
 * LinearInterpolationThermistor.cpp
 *
 * Copyright 2012 Pieter Agten
 *
 * This file is part of tltgen.
 *
 * tltgen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * tltgen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with tltgen.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file   LinearInterpolationThermistor.cpp
 * @author Pieter Agten (pieter.agten@gmail.com)
 * @date   11 feb 2013
 */

#include <list>
#include <string>
#include <stdexcept>
#include <boost/lexical_cast.hpp>
#include <boost/tuple/tuple.hpp>

#include "ThermistorTable.hpp"
#include "ThermistorTableRow.hpp"
#include "ThermistorVisitor.hpp"

#include "LinearInterpolationThermistor.hpp"

namespace tltgen {

  using boost::tuple;
  using boost::make_tuple;

  LinearInterpolationThermistor::LinearInterpolationThermistor(const shared_ptr<ThermistorTable>& thermistorTable)
  {
    if (! isValidThermistorTable(thermistorTable)) {
      throw std::invalid_argument("Invalid thermistor table specified.");
    }

    for (std::list<ThermistorTableRow *>::iterator it = thermistorTable->begin(); it != thermistorTable->end(); ++it) {
      if ((*it)->hasValidTemperature() && (*it)->hasValidResistance()) {
	Temperature kelvin_t = (*it)->getTemperature();
	Resistance r = (*it)->getResistance();
	this->temperatureResistanceValues.push_back(make_tuple(kelvin_t, r));
	this->resistanceTemperatureValues.push_back(make_tuple(r, kelvin_t));
      }
    }
    std::sort(this->temperatureResistanceValues.begin(), this->temperatureResistanceValues.end(), compareFirst);
    std::sort(this->resistanceTemperatureValues.begin(), this->resistanceTemperatureValues.end(), compareFirst);
  }
  LinearInterpolationThermistor::~LinearInterpolationThermistor()
  { }

  bool LinearInterpolationThermistor::compareFirst(tuple<double,double> t1, tuple<double,double> t2)
  {
    return t1.get<0>() < t2.get<0>();
  }

  bool LinearInterpolationThermistor::isValidThermistorTable(const shared_ptr<ThermistorTable>& thermistorTable)
  {
    return thermistorTable.get() != NULL;
  }

  bool LinearInterpolationThermistor::isValidParameterIndex(unsigned int pi) const
  {
    return false;
  }
  double LinearInterpolationThermistor::getParameterValue(unsigned int pi) const
  {
    throw std::invalid_argument("Parameter index '" + boost::lexical_cast<std::string>(pi) + "' is invalid.");
  }

  bool LinearInterpolationThermistor::isValidResistance(Resistance r) const
  {
    return tltgen::isValidResistance(r);
  }
  Temperature LinearInterpolationThermistor::getTemperature(Resistance r) const
  {
    if (this->resistanceTemperatureValues.empty()) {
      return NAN;
    }

    std::vector<tuple<Resistance,Temperature> >::const_iterator it = this->resistanceTemperatureValues.begin();
    if (r < it->get<0>()) {
      return NAN;
    }
    while (it != this->resistanceTemperatureValues.end() && r >= it->get<0>()) {
      ++it;
    }
    if (it == this->resistanceTemperatureValues.end()) {
      return NAN;
    }

    Resistance r0 = (it-1)->get<0>();
    Temperature t0 = (it-1)->get<1>();
    Resistance r1 = it->get<0>();
    Temperature t1 = it->get<1>();
    
    return t0 + (r - r0)/(r1 - r0)*(t1-t0);
  }

  bool LinearInterpolationThermistor::isValidTemperature(Temperature t) const
  {
    return tltgen::isValidKelvinTemperature(t) && t >= 0;
  }
  Resistance LinearInterpolationThermistor::getResistance(Temperature t) const
  {
    if (this->temperatureResistanceValues.empty()) {
      return NAN;
    }

    std::vector<tuple<Temperature,Resistance> >::const_iterator it = this->temperatureResistanceValues.begin();
    if (t < it->get<0>()) {
      return NAN;
    }
    while (it != this->temperatureResistanceValues.end() && t >= it->get<0>()) {
      ++it;
    }
    if (it == this->temperatureResistanceValues.end()) {
      return NAN;
    }

    Resistance t0 = (it-1)->get<0>();
    Temperature r0 = (it-1)->get<1>();
    Resistance t1 = it->get<0>();
    Temperature r1 = it->get<1>();
    
    return r0 + (t - t0)/(t1 - t0)*(r1-r0);
  }

  void LinearInterpolationThermistor::accept(ThermistorVisitor* visitor) const
  {
    visitor->visit(this);
  }

  std::ostream& LinearInterpolationThermistor::print(std::ostream &stream) const
  {
    return stream << "Linear interpolation thermistor.";
  }

}
